/*
 * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.awt.image;

import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import sun.java2d.cmm.CMSManager;
import sun.java2d.cmm.ColorTransform;
import sun.java2d.cmm.PCMM;
import java.awt.Toolkit;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;

/**
 * The <code>ColorModel</code> abstract class encapsulates the
 * methods for translating a pixel value to color components
 * (for example, red, green, and blue) and an alpha component.
 * In order to render an image to the screen, a printer, or another
 * image, pixel values must be converted to color and alpha components.
 * As arguments to or return values from methods of this class,
 * pixels are represented as 32-bit ints or as arrays of primitive types.
 * The number, order, and interpretation of color components for a
 * <code>ColorModel</code> is specified by its <code>ColorSpace</code>.
 * A <code>ColorModel</code> used with pixel data that does not include
 * alpha information treats all pixels as opaque, which is an alpha
 * value of 1.0.
 * <p>
 * This <code>ColorModel</code> class supports two representations of
 * pixel values.  A pixel value can be a single 32-bit int or an
 * array of primitive types.  The Java(tm) Platform 1.0 and 1.1 APIs
 * represented pixels as single <code>byte</code> or single
 * <code>int</code> values.  For purposes of the <code>ColorModel</code>
 * class, pixel value arguments were passed as ints.  The Java(tm) 2
 * Platform API introduced additional classes for representing images.
 * With {@link BufferedImage} or {@link RenderedImage}
 * objects, based on {@link Raster} and {@link SampleModel} classes, pixel
 * values might not be conveniently representable as a single int.
 * Consequently, <code>ColorModel</code> now has methods that accept
 * pixel values represented as arrays of primitive types.  The primitive
 * type used by a particular <code>ColorModel</code> object is called its
 * transfer type.
 * <p>
 * <code>ColorModel</code> objects used with images for which pixel values
 * are not conveniently representable as a single int throw an
 * {@link IllegalArgumentException} when methods taking a single int pixel
 * argument are called.  Subclasses of <code>ColorModel</code> must
 * specify the conditions under which this occurs.  This does not
 * occur with {@link DirectColorModel} or {@link IndexColorModel} objects.
 * <p>
 * Currently, the transfer types supported by the Java 2D(tm) API are
 * DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT, DataBuffer.TYPE_INT,
 * DataBuffer.TYPE_SHORT, DataBuffer.TYPE_FLOAT, and DataBuffer.TYPE_DOUBLE.
 * Most rendering operations will perform much faster when using ColorModels
 * and images based on the first three of these types.  In addition, some
 * image filtering operations are not supported for ColorModels and
 * images based on the latter three types.
 * The transfer type for a particular <code>ColorModel</code> object is
 * specified when the object is created, either explicitly or by default.
 * All subclasses of <code>ColorModel</code> must specify what the
 * possible transfer types are and how the number of elements in the
 * primitive arrays representing pixels is determined.
 * <p>
 * For <code>BufferedImages</code>, the transfer type of its
 * <code>Raster</code> and of the <code>Raster</code> object's
 * <code>SampleModel</code> (available from the
 * <code>getTransferType</code> methods of these classes) must match that
 * of the <code>ColorModel</code>.  The number of elements in an array
 * representing a pixel for the <code>Raster</code> and
 * <code>SampleModel</code> (available from the
 * <code>getNumDataElements</code> methods of these classes) must match
 * that of the <code>ColorModel</code>.
 * <p>
 * The algorithm used to convert from pixel values to color and alpha
 * components varies by subclass.  For example, there is not necessarily
 * a one-to-one correspondence between samples obtained from the
 * <code>SampleModel</code> of a <code>BufferedImage</code> object's
 * <code>Raster</code> and color/alpha components.  Even when
 * there is such a correspondence, the number of bits in a sample is not
 * necessarily the same as the number of bits in the corresponding color/alpha
 * component.  Each subclass must specify how the translation from
 * pixel values to color/alpha components is done.
 * <p>
 * Methods in the <code>ColorModel</code> class use two different
 * representations of color and alpha components - a normalized form
 * and an unnormalized form.  In the normalized form, each component is a
 * <code>float</code> value between some minimum and maximum values.  For
 * the alpha component, the minimum is 0.0 and the maximum is 1.0.  For
 * color components the minimum and maximum values for each component can
 * be obtained from the <code>ColorSpace</code> object.  These values
 * will often be 0.0 and 1.0 (e.g. normalized component values for the
 * default sRGB color space range from 0.0 to 1.0), but some color spaces
 * have component values with different upper and lower limits.  These
 * limits can be obtained using the <code>getMinValue</code> and
 * <code>getMaxValue</code> methods of the <code>ColorSpace</code>
 * class.  Normalized color component values are not premultiplied.
 * All <code>ColorModels</code> must support the normalized form.
 * <p>
 * In the unnormalized
 * form, each component is an unsigned integral value between 0 and
 * 2<sup>n</sup> - 1, where n is the number of significant bits for a
 * particular component.  If pixel values for a particular
 * <code>ColorModel</code> represent color samples premultiplied by
 * the alpha sample, unnormalized color component values are
 * also premultiplied.  The unnormalized form is used only with instances
 * of <code>ColorModel</code> whose <code>ColorSpace</code> has minimum
 * component values of 0.0 for all components and maximum values of
 * 1.0 for all components.
 * The unnormalized form for color and alpha components can be a convenient
 * representation for <code>ColorModels</code> whose normalized component
 * values all lie
 * between 0.0 and 1.0.  In such cases the integral value 0 maps to 0.0 and
 * the value 2<sup>n</sup> - 1 maps to 1.0.  In other cases, such as
 * when the normalized component values can be either negative or positive,
 * the unnormalized form is not convenient.  Such <code>ColorModel</code>
 * objects throw an {@link IllegalArgumentException} when methods involving
 * an unnormalized argument are called.  Subclasses of <code>ColorModel</code>
 * must specify the conditions under which this occurs.
 *
 * @see IndexColorModel
 * @see ComponentColorModel
 * @see PackedColorModel
 * @see DirectColorModel
 * @see java.awt.Image
 * @see BufferedImage
 * @see RenderedImage
 * @see java.awt.color.ColorSpace
 * @see SampleModel
 * @see Raster
 * @see DataBuffer
 */
public abstract class ColorModel implements Transparency {

  private long pData;         // Placeholder for data for native functions

  /**
   * The total number of bits in the pixel.
   */
  protected int pixel_bits;
  int nBits[];
  int transparency = Transparency.TRANSLUCENT;
  boolean supportsAlpha = true;
  boolean isAlphaPremultiplied = false;
  int numComponents = -1;
  int numColorComponents = -1;
  ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
  int colorSpaceType = ColorSpace.TYPE_RGB;
  int maxBits;
  boolean is_sRGB = true;

  /**
   * Data type of the array used to represent pixel values.
   */
  protected int transferType;

  /**
   * This is copied from java.awt.Toolkit since we need the library
   * loaded in java.awt.image also:
   *
   * WARNING: This is a temporary workaround for a problem in the
   * way the AWT loads native libraries. A number of classes in the
   * AWT package have a native method, initIDs(), which initializes
   * the JNI field and method ids used in the native portion of
   * their implementation.
   *
   * Since the use and storage of these ids is done by the
   * implementation libraries, the implementation of these method is
   * provided by the particular AWT implementations (for example,
   * "Toolkit"s/Peer), such as Motif, Microsoft Windows, or Tiny. The
   * problem is that this means that the native libraries must be
   * loaded by the java.* classes, which do not necessarily know the
   * names of the libraries to load. A better way of doing this
   * would be to provide a separate library which defines java.awt.*
   * initIDs, and exports the relevant symbols out to the
   * implementation libraries.
   *
   * For now, we know it's done by the implementation, and we assume
   * that the name of the library is "awt".  -br.
   */
  private static boolean loaded = false;

  static void loadLibraries() {
    if (!loaded) {
      java.security.AccessController.doPrivileged(
          new java.security.PrivilegedAction<Void>() {
            public Void run() {
              System.loadLibrary("awt");
              return null;
            }
          });
      loaded = true;
    }
  }

  private static native void initIDs();

  static {
        /* ensure that the proper libraries are loaded */
    loadLibraries();
    initIDs();
  }

  private static ColorModel RGBdefault;

  /**
   * Returns a <code>DirectColorModel</code> that describes the default
   * format for integer RGB values used in many of the methods in the
   * AWT image interfaces for the convenience of the programmer.
   * The color space is the default {@link ColorSpace}, sRGB.
   * The format for the RGB values is an integer with 8 bits
   * each of alpha, red, green, and blue color components ordered
   * correspondingly from the most significant byte to the least
   * significant byte, as in:  0xAARRGGBB.  Color components are
   * not premultiplied by the alpha component.  This format does not
   * necessarily represent the native or the most efficient
   * <code>ColorModel</code> for a particular device or for all images.
   * It is merely used as a common color model format.
   *
   * @return a <code>DirectColorModel</code>object describing default RGB values.
   */
  public static ColorModel getRGBdefault() {
    if (RGBdefault == null) {
      RGBdefault = new DirectColorModel(32,
          0x00ff0000,       // Red
          0x0000ff00,       // Green
          0x000000ff,       // Blue
          0xff000000        // Alpha
      );
    }
    return RGBdefault;
  }

  /**
   * Constructs a <code>ColorModel</code> that translates pixels of the
   * specified number of bits to color/alpha components.  The color
   * space is the default RGB <code>ColorSpace</code>, which is sRGB.
   * Pixel values are assumed to include alpha information.  If color
   * and alpha information are represented in the pixel value as
   * separate spatial bands, the color bands are assumed not to be
   * premultiplied with the alpha value. The transparency type is
   * java.awt.Transparency.TRANSLUCENT.  The transfer type will be the
   * smallest of DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT,
   * or DataBuffer.TYPE_INT that can hold a single pixel
   * (or DataBuffer.TYPE_UNDEFINED if bits is greater
   * than 32).  Since this constructor has no information about the
   * number of bits per color and alpha component, any subclass calling
   * this constructor should override any method that requires this
   * information.
   *
   * @param bits the number of bits of a pixel
   * @throws IllegalArgumentException if the number of bits in <code>bits</code> is less than 1
   */
  public ColorModel(int bits) {
    pixel_bits = bits;
    if (bits < 1) {
      throw new IllegalArgumentException("Number of bits must be > 0");
    }
    numComponents = 4;
    numColorComponents = 3;
    maxBits = bits;
    // REMIND: make sure transferType is set correctly
    transferType = ColorModel.getDefaultTransferType(bits);
  }

  /**
   * Constructs a <code>ColorModel</code> that translates pixel values
   * to color/alpha components.  Color components will be in the
   * specified <code>ColorSpace</code>. <code>pixel_bits</code> is the
   * number of bits in the pixel values.  The bits array
   * specifies the number of significant bits per color and alpha component.
   * Its length should be the number of components in the
   * <code>ColorSpace</code> if there is no alpha information in the
   * pixel values, or one more than this number if there is alpha
   * information.  <code>hasAlpha</code> indicates whether or not alpha
   * information is present.  The <code>boolean</code>
   * <code>isAlphaPremultiplied</code> specifies how to interpret pixel
   * values in which color and alpha information are represented as
   * separate spatial bands.  If the <code>boolean</code>
   * is <code>true</code>, color samples are assumed to have been
   * multiplied by the alpha sample.  The <code>transparency</code>
   * specifies what alpha values can be represented by this color model.
   * The transfer type is the type of primitive array used to represent
   * pixel values.  Note that the bits array contains the number of
   * significant bits per color/alpha component after the translation
   * from pixel values.  For example, for an
   * <code>IndexColorModel</code> with <code>pixel_bits</code> equal to
   * 16, the bits array might have four elements with each element set
   * to 8.
   *
   * @param pixel_bits the number of bits in the pixel values
   * @param bits array that specifies the number of significant bits per color and alpha component
   * @param cspace the specified <code>ColorSpace</code>
   * @param hasAlpha <code>true</code> if alpha information is present; <code>false</code>
   * otherwise
   * @param isAlphaPremultiplied <code>true</code> if color samples are assumed to be premultiplied
   * by the alpha samples; <code>false</code> otherwise
   * @param transparency what alpha values can be represented by this color model
   * @param transferType the type of the array used to represent pixel values
   * @throws IllegalArgumentException if the length of the bit array is less than the number of
   * color or alpha components in this <code>ColorModel</code>, or if the transparency is not a
   * valid value.
   * @throws IllegalArgumentException if the sum of the number of bits in <code>bits</code> is less
   * than 1 or if any of the elements in <code>bits</code> is less than 0.
   * @see java.awt.Transparency
   */
  protected ColorModel(int pixel_bits, int[] bits, ColorSpace cspace,
      boolean hasAlpha,
      boolean isAlphaPremultiplied,
      int transparency,
      int transferType) {
    colorSpace = cspace;
    colorSpaceType = cspace.getType();
    numColorComponents = cspace.getNumComponents();
    numComponents = numColorComponents + (hasAlpha ? 1 : 0);
    supportsAlpha = hasAlpha;
    if (bits.length < numComponents) {
      throw new IllegalArgumentException("Number of color/alpha " +
          "components should be " +
          numComponents +
          " but length of bits array is " +
          bits.length);
    }

    // 4186669
    if (transparency < Transparency.OPAQUE ||
        transparency > Transparency.TRANSLUCENT) {
      throw new IllegalArgumentException("Unknown transparency: " +
          transparency);
    }

    if (supportsAlpha == false) {
      this.isAlphaPremultiplied = false;
      this.transparency = Transparency.OPAQUE;
    } else {
      this.isAlphaPremultiplied = isAlphaPremultiplied;
      this.transparency = transparency;
    }

    nBits = bits.clone();
    this.pixel_bits = pixel_bits;
    if (pixel_bits <= 0) {
      throw new IllegalArgumentException("Number of pixel bits must " +
          "be > 0");
    }
    // Check for bits < 0
    maxBits = 0;
    for (int i = 0; i < bits.length; i++) {
      // bug 4304697
      if (bits[i] < 0) {
        throw new
            IllegalArgumentException("Number of bits must be >= 0");
      }
      if (maxBits < bits[i]) {
        maxBits = bits[i];
      }
    }

    // Make sure that we don't have all 0-bit components
    if (maxBits == 0) {
      throw new IllegalArgumentException("There must be at least " +
          "one component with > 0 " +
          "pixel bits.");
    }

    // Save this since we always need to check if it is the default CS
    if (cspace != ColorSpace.getInstance(ColorSpace.CS_sRGB)) {
      is_sRGB = false;
    }

    // Save the transfer type
    this.transferType = transferType;
  }

  /**
   * Returns whether or not alpha is supported in this
   * <code>ColorModel</code>.
   *
   * @return <code>true</code> if alpha is supported in this <code>ColorModel</code>;
   * <code>false</code> otherwise.
   */
  final public boolean hasAlpha() {
    return supportsAlpha;
  }

  /**
   * Returns whether or not the alpha has been premultiplied in the
   * pixel values to be translated by this <code>ColorModel</code>.
   * If the boolean is <code>true</code>, this <code>ColorModel</code>
   * is to be used to interpret pixel values in which color and alpha
   * information are represented as separate spatial bands, and color
   * samples are assumed to have been multiplied by the
   * alpha sample.
   *
   * @return <code>true</code> if the alpha values are premultiplied in the pixel values to be
   * translated by this <code>ColorModel</code>; <code>false</code> otherwise.
   */
  final public boolean isAlphaPremultiplied() {
    return isAlphaPremultiplied;
  }

  /**
   * Returns the transfer type of this <code>ColorModel</code>.
   * The transfer type is the type of primitive array used to represent
   * pixel values as arrays.
   *
   * @return the transfer type.
   * @since 1.3
   */
  final public int getTransferType() {
    return transferType;
  }

  /**
   * Returns the number of bits per pixel described by this
   * <code>ColorModel</code>.
   *
   * @return the number of bits per pixel.
   */
  public int getPixelSize() {
    return pixel_bits;
  }

  /**
   * Returns the number of bits for the specified color/alpha component.
   * Color components are indexed in the order specified by the
   * <code>ColorSpace</code>.  Typically, this order reflects the name
   * of the color space type. For example, for TYPE_RGB, index 0
   * corresponds to red, index 1 to green, and index 2
   * to blue.  If this <code>ColorModel</code> supports alpha, the alpha
   * component corresponds to the index following the last color
   * component.
   *
   * @param componentIdx the index of the color/alpha component
   * @return the number of bits for the color/alpha component at the specified index.
   * @throws ArrayIndexOutOfBoundsException if <code>componentIdx</code> is greater than the number
   * of components or less than zero
   * @throws NullPointerException if the number of bits array is <code>null</code>
   */
  public int getComponentSize(int componentIdx) {
    // REMIND:
    if (nBits == null) {
      throw new NullPointerException("Number of bits array is null.");
    }

    return nBits[componentIdx];
  }

  /**
   * Returns an array of the number of bits per color/alpha component.
   * The array contains the color components in the order specified by the
   * <code>ColorSpace</code>, followed by the alpha component, if
   * present.
   *
   * @return an array of the number of bits per color/alpha component
   */
  public int[] getComponentSize() {
    if (nBits != null) {
      return nBits.clone();
    }

    return null;
  }

  /**
   * Returns the transparency.  Returns either OPAQUE, BITMASK,
   * or TRANSLUCENT.
   *
   * @return the transparency of this <code>ColorModel</code>.
   * @see Transparency#OPAQUE
   * @see Transparency#BITMASK
   * @see Transparency#TRANSLUCENT
   */
  public int getTransparency() {
    return transparency;
  }

  /**
   * Returns the number of components, including alpha, in this
   * <code>ColorModel</code>.  This is equal to the number of color
   * components, optionally plus one, if there is an alpha component.
   *
   * @return the number of components in this <code>ColorModel</code>
   */
  public int getNumComponents() {
    return numComponents;
  }

  /**
   * Returns the number of color components in this
   * <code>ColorModel</code>.
   * This is the number of components returned by
   * {@link ColorSpace#getNumComponents}.
   *
   * @return the number of color components in this <code>ColorModel</code>.
   * @see ColorSpace#getNumComponents
   */
  public int getNumColorComponents() {
    return numColorComponents;
  }

  /**
   * Returns the red color component for the specified pixel, scaled
   * from 0 to 255 in the default RGB ColorSpace, sRGB.  A color conversion
   * is done if necessary.  The pixel value is specified as an int.
   * An <code>IllegalArgumentException</code> is thrown if pixel
   * values for this <code>ColorModel</code> are not conveniently
   * representable as a single int.  The returned value is not a
   * pre-multiplied value.  For example, if the
   * alpha is premultiplied, this method divides it out before returning
   * the value.  If the alpha value is 0, the red value is 0.
   *
   * @param pixel a specified pixel
   * @return the value of the red component of the specified pixel.
   */
  public abstract int getRed(int pixel);

  /**
   * Returns the green color component for the specified pixel, scaled
   * from 0 to 255 in the default RGB ColorSpace, sRGB.  A color conversion
   * is done if necessary.  The pixel value is specified as an int.
   * An <code>IllegalArgumentException</code> is thrown if pixel
   * values for this <code>ColorModel</code> are not conveniently
   * representable as a single int.  The returned value is a non
   * pre-multiplied value.  For example, if the alpha is premultiplied,
   * this method divides it out before returning
   * the value.  If the alpha value is 0, the green value is 0.
   *
   * @param pixel the specified pixel
   * @return the value of the green component of the specified pixel.
   */
  public abstract int getGreen(int pixel);

  /**
   * Returns the blue color component for the specified pixel, scaled
   * from 0 to 255 in the default RGB ColorSpace, sRGB.  A color conversion
   * is done if necessary.  The pixel value is specified as an int.
   * An <code>IllegalArgumentException</code> is thrown if pixel values
   * for this <code>ColorModel</code> are not conveniently representable
   * as a single int.  The returned value is a non pre-multiplied
   * value, for example, if the alpha is premultiplied, this method
   * divides it out before returning the value.  If the alpha value is
   * 0, the blue value is 0.
   *
   * @param pixel the specified pixel
   * @return the value of the blue component of the specified pixel.
   */
  public abstract int getBlue(int pixel);

  /**
   * Returns the alpha component for the specified pixel, scaled
   * from 0 to 255.  The pixel value is specified as an int.
   * An <code>IllegalArgumentException</code> is thrown if pixel
   * values for this <code>ColorModel</code> are not conveniently
   * representable as a single int.
   *
   * @param pixel the specified pixel
   * @return the value of alpha component of the specified pixel.
   */
  public abstract int getAlpha(int pixel);

  /**
   * Returns the color/alpha components of the pixel in the default
   * RGB color model format.  A color conversion is done if necessary.
   * The pixel value is specified as an int.
   * An <code>IllegalArgumentException</code> thrown if pixel values
   * for this <code>ColorModel</code> are not conveniently representable
   * as a single int.  The returned value is in a non
   * pre-multiplied format. For example, if the alpha is premultiplied,
   * this method divides it out of the color components.  If the alpha
   * value is 0, the color values are 0.
   *
   * @param pixel the specified pixel
   * @return the RGB value of the color/alpha components of the specified pixel.
   * @see ColorModel#getRGBdefault
   */
  public int getRGB(int pixel) {
    return (getAlpha(pixel) << 24)
        | (getRed(pixel) << 16)
        | (getGreen(pixel) << 8)
        | (getBlue(pixel) << 0);
  }

  /**
   * Returns the red color component for the specified pixel, scaled
   * from 0 to 255 in the default RGB <code>ColorSpace</code>, sRGB.  A
   * color conversion is done if necessary.  The pixel value is
   * specified by an array of data elements of type transferType passed
   * in as an object reference.  The returned value is a non
   * pre-multiplied value.  For example, if alpha is premultiplied,
   * this method divides it out before returning
   * the value.  If the alpha value is 0, the red value is 0.
   * If <code>inData</code> is not a primitive array of type
   * transferType, a <code>ClassCastException</code> is thrown.  An
   * <code>ArrayIndexOutOfBoundsException</code> is thrown if
   * <code>inData</code> is not large enough to hold a pixel value for
   * this <code>ColorModel</code>.
   * If this <code>transferType</code> is not supported, a
   * <code>UnsupportedOperationException</code> will be
   * thrown.  Since
   * <code>ColorModel</code> is an abstract class, any instance
   * must be an instance of a subclass.  Subclasses inherit the
   * implementation of this method and if they don't override it, this
   * method throws an exception if the subclass uses a
   * <code>transferType</code> other than
   * <code>DataBuffer.TYPE_BYTE</code>,
   * <code>DataBuffer.TYPE_USHORT</code>, or
   * <code>DataBuffer.TYPE_INT</code>.
   *
   * @param inData an array of pixel values
   * @return the value of the red component of the specified pixel.
   * @throws ClassCastException if <code>inData</code> is not a primitive array of type
   * <code>transferType</code>
   * @throws ArrayIndexOutOfBoundsException if <code>inData</code> is not large enough to hold a
   * pixel value for this <code>ColorModel</code>
   * @throws UnsupportedOperationException if this <code>tranferType</code> is not supported by this
   * <code>ColorModel</code>
   */
  public int getRed(Object inData) {
    int pixel = 0, length = 0;
    switch (transferType) {
      case DataBuffer.TYPE_BYTE:
        byte bdata[] = (byte[]) inData;
        pixel = bdata[0] & 0xff;
        length = bdata.length;
        break;
      case DataBuffer.TYPE_USHORT:
        short sdata[] = (short[]) inData;
        pixel = sdata[0] & 0xffff;
        length = sdata.length;
        break;
      case DataBuffer.TYPE_INT:
        int idata[] = (int[]) inData;
        pixel = idata[0];
        length = idata.length;
        break;
      default:
        throw new UnsupportedOperationException("This method has not been " +
            "implemented for transferType " + transferType);
    }
    if (length == 1) {
      return getRed(pixel);
    } else {
      throw new UnsupportedOperationException
          ("This method is not supported by this color model");
    }
  }

  /**
   * Returns the green color component for the specified pixel, scaled
   * from 0 to 255 in the default RGB <code>ColorSpace</code>, sRGB.  A
   * color conversion is done if necessary.  The pixel value is
   * specified by an array of data elements of type transferType passed
   * in as an object reference.  The returned value will be a non
   * pre-multiplied value.  For example, if the alpha is premultiplied,
   * this method divides it out before returning the value.  If the
   * alpha value is 0, the green value is 0.  If <code>inData</code> is
   * not a primitive array of type transferType, a
   * <code>ClassCastException</code> is thrown.  An
   * <code>ArrayIndexOutOfBoundsException</code> is thrown if
   * <code>inData</code> is not large enough to hold a pixel value for
   * this <code>ColorModel</code>.
   * If this <code>transferType</code> is not supported, a
   * <code>UnsupportedOperationException</code> will be
   * thrown.  Since
   * <code>ColorModel</code> is an abstract class, any instance
   * must be an instance of a subclass.  Subclasses inherit the
   * implementation of this method and if they don't override it, this
   * method throws an exception if the subclass uses a
   * <code>transferType</code> other than
   * <code>DataBuffer.TYPE_BYTE</code>,
   * <code>DataBuffer.TYPE_USHORT</code>, or
   * <code>DataBuffer.TYPE_INT</code>.
   *
   * @param inData an array of pixel values
   * @return the value of the green component of the specified pixel.
   * @throws ClassCastException if <code>inData</code> is not a primitive array of type
   * <code>transferType</code>
   * @throws ArrayIndexOutOfBoundsException if <code>inData</code> is not large enough to hold a
   * pixel value for this <code>ColorModel</code>
   * @throws UnsupportedOperationException if this <code>tranferType</code> is not supported by this
   * <code>ColorModel</code>
   */
  public int getGreen(Object inData) {
    int pixel = 0, length = 0;
    switch (transferType) {
      case DataBuffer.TYPE_BYTE:
        byte bdata[] = (byte[]) inData;
        pixel = bdata[0] & 0xff;
        length = bdata.length;
        break;
      case DataBuffer.TYPE_USHORT:
        short sdata[] = (short[]) inData;
        pixel = sdata[0] & 0xffff;
        length = sdata.length;
        break;
      case DataBuffer.TYPE_INT:
        int idata[] = (int[]) inData;
        pixel = idata[0];
        length = idata.length;
        break;
      default:
        throw new UnsupportedOperationException("This method has not been " +
            "implemented for transferType " + transferType);
    }
    if (length == 1) {
      return getGreen(pixel);
    } else {
      throw new UnsupportedOperationException
          ("This method is not supported by this color model");
    }
  }

  /**
   * Returns the blue color component for the specified pixel, scaled
   * from 0 to 255 in the default RGB <code>ColorSpace</code>, sRGB.  A
   * color conversion is done if necessary.  The pixel value is
   * specified by an array of data elements of type transferType passed
   * in as an object reference.  The returned value is a non
   * pre-multiplied value.  For example, if the alpha is premultiplied,
   * this method divides it out before returning the value.  If the
   * alpha value is 0, the blue value will be 0.  If
   * <code>inData</code> is not a primitive array of type transferType,
   * a <code>ClassCastException</code> is thrown.  An
   * <code>ArrayIndexOutOfBoundsException</code> is
   * thrown if <code>inData</code> is not large enough to hold a pixel
   * value for this <code>ColorModel</code>.
   * If this <code>transferType</code> is not supported, a
   * <code>UnsupportedOperationException</code> will be
   * thrown.  Since
   * <code>ColorModel</code> is an abstract class, any instance
   * must be an instance of a subclass.  Subclasses inherit the
   * implementation of this method and if they don't override it, this
   * method throws an exception if the subclass uses a
   * <code>transferType</code> other than
   * <code>DataBuffer.TYPE_BYTE</code>,
   * <code>DataBuffer.TYPE_USHORT</code>, or
   * <code>DataBuffer.TYPE_INT</code>.
   *
   * @param inData an array of pixel values
   * @return the value of the blue component of the specified pixel.
   * @throws ClassCastException if <code>inData</code> is not a primitive array of type
   * <code>transferType</code>
   * @throws ArrayIndexOutOfBoundsException if <code>inData</code> is not large enough to hold a
   * pixel value for this <code>ColorModel</code>
   * @throws UnsupportedOperationException if this <code>tranferType</code> is not supported by this
   * <code>ColorModel</code>
   */
  public int getBlue(Object inData) {
    int pixel = 0, length = 0;
    switch (transferType) {
      case DataBuffer.TYPE_BYTE:
        byte bdata[] = (byte[]) inData;
        pixel = bdata[0] & 0xff;
        length = bdata.length;
        break;
      case DataBuffer.TYPE_USHORT:
        short sdata[] = (short[]) inData;
        pixel = sdata[0] & 0xffff;
        length = sdata.length;
        break;
      case DataBuffer.TYPE_INT:
        int idata[] = (int[]) inData;
        pixel = idata[0];
        length = idata.length;
        break;
      default:
        throw new UnsupportedOperationException("This method has not been " +
            "implemented for transferType " + transferType);
    }
    if (length == 1) {
      return getBlue(pixel);
    } else {
      throw new UnsupportedOperationException
          ("This method is not supported by this color model");
    }
  }

  /**
   * Returns the alpha component for the specified pixel, scaled
   * from 0 to 255.  The pixel value is specified by an array of data
   * elements of type transferType passed in as an object reference.
   * If inData is not a primitive array of type transferType, a
   * <code>ClassCastException</code> is thrown.  An
   * <code>ArrayIndexOutOfBoundsException</code> is thrown if
   * <code>inData</code> is not large enough to hold a pixel value for
   * this <code>ColorModel</code>.
   * If this <code>transferType</code> is not supported, a
   * <code>UnsupportedOperationException</code> will be
   * thrown.  Since
   * <code>ColorModel</code> is an abstract class, any instance
   * must be an instance of a subclass.  Subclasses inherit the
   * implementation of this method and if they don't override it, this
   * method throws an exception if the subclass uses a
   * <code>transferType</code> other than
   * <code>DataBuffer.TYPE_BYTE</code>,
   * <code>DataBuffer.TYPE_USHORT</code>, or
   * <code>DataBuffer.TYPE_INT</code>.
   *
   * @param inData the specified pixel
   * @return the alpha component of the specified pixel, scaled from 0 to 255.
   * @throws ClassCastException if <code>inData</code> is not a primitive array of type
   * <code>transferType</code>
   * @throws ArrayIndexOutOfBoundsException if <code>inData</code> is not large enough to hold a
   * pixel value for this <code>ColorModel</code>
   * @throws UnsupportedOperationException if this <code>tranferType</code> is not supported by this
   * <code>ColorModel</code>
   */
  public int getAlpha(Object inData) {
    int pixel = 0, length = 0;
    switch (transferType) {
      case DataBuffer.TYPE_BYTE:
        byte bdata[] = (byte[]) inData;
        pixel = bdata[0] & 0xff;
        length = bdata.length;
        break;
      case DataBuffer.TYPE_USHORT:
        short sdata[] = (short[]) inData;
        pixel = sdata[0] & 0xffff;
        length = sdata.length;
        break;
      case DataBuffer.TYPE_INT:
        int idata[] = (int[]) inData;
        pixel = idata[0];
        length = idata.length;
        break;
      default:
        throw new UnsupportedOperationException("This method has not been " +
            "implemented for transferType " + transferType);
    }
    if (length == 1) {
      return getAlpha(pixel);
    } else {
      throw new UnsupportedOperationException
          ("This method is not supported by this color model");
    }
  }

  /**
   * Returns the color/alpha components for the specified pixel in the
   * default RGB color model format.  A color conversion is done if
   * necessary.  The pixel value is specified by an array of data
   * elements of type transferType passed in as an object reference.
   * If inData is not a primitive array of type transferType, a
   * <code>ClassCastException</code> is thrown.  An
   * <code>ArrayIndexOutOfBoundsException</code> is
   * thrown if <code>inData</code> is not large enough to hold a pixel
   * value for this <code>ColorModel</code>.
   * The returned value will be in a non pre-multiplied format, i.e. if
   * the alpha is premultiplied, this method will divide it out of the
   * color components (if the alpha value is 0, the color values will be 0).
   *
   * @param inData the specified pixel
   * @return the color and alpha components of the specified pixel.
   * @see ColorModel#getRGBdefault
   */
  public int getRGB(Object inData) {
    return (getAlpha(inData) << 24)
        | (getRed(inData) << 16)
        | (getGreen(inData) << 8)
        | (getBlue(inData) << 0);
  }

  /**
   * Returns a data element array representation of a pixel in this
   * <code>ColorModel</code>, given an integer pixel representation in
   * the default RGB color model.
   * This array can then be passed to the
   * {@link WritableRaster#setDataElements} method of
   * a {@link WritableRaster} object.  If the pixel variable is
   * <code>null</code>, a new array will be allocated.  If
   * <code>pixel</code> is not
   * <code>null</code>, it must be a primitive array of type
   * <code>transferType</code>; otherwise, a
   * <code>ClassCastException</code> is thrown.  An
   * <code>ArrayIndexOutOfBoundsException</code> is thrown if
   * <code>pixel</code> is
   * not large enough to hold a pixel value for this
   * <code>ColorModel</code>. The pixel array is returned.
   * If this <code>transferType</code> is not supported, a
   * <code>UnsupportedOperationException</code> will be
   * thrown.  Since <code>ColorModel</code> is an abstract class,
   * any instance is an instance of a subclass.  Subclasses must
   * override this method since the implementation in this abstract
   * class throws an <code>UnsupportedOperationException</code>.
   *
   * @param rgb the integer pixel representation in the default RGB color model
   * @param pixel the specified pixel
   * @return an array representation of the specified pixel in this <code>ColorModel</code>.
   * @throws ClassCastException if <code>pixel</code> is not a primitive array of type
   * <code>transferType</code>
   * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is not large enough to hold a
   * pixel value for this <code>ColorModel</code>
   * @throws UnsupportedOperationException if this method is not supported by this
   * <code>ColorModel</code>
   * @see WritableRaster#setDataElements
   * @see SampleModel#setDataElements
   */
  public Object getDataElements(int rgb, Object pixel) {
    throw new UnsupportedOperationException
        ("This method is not supported by this color model.");
  }

  /**
   * Returns an array of unnormalized color/alpha components given a pixel
   * in this <code>ColorModel</code>.  The pixel value is specified as
   * an <code>int</code>.  An <code>IllegalArgumentException</code>
   * will be thrown if pixel values for this <code>ColorModel</code> are
   * not conveniently representable as a single <code>int</code> or if
   * color component values for this <code>ColorModel</code> are not
   * conveniently representable in the unnormalized form.
   * For example, this method can be used to retrieve the
   * components for a specific pixel value in a
   * <code>DirectColorModel</code>.  If the components array is
   * <code>null</code>, a new array will be allocated.  The
   * components array will be returned.  Color/alpha components are
   * stored in the components array starting at <code>offset</code>
   * (even if the array is allocated by this method).  An
   * <code>ArrayIndexOutOfBoundsException</code> is thrown if  the
   * components array is not <code>null</code> and is not large
   * enough to hold all the color and alpha components (starting at offset).
   * Since <code>ColorModel</code> is an abstract class,
   * any instance is an instance of a subclass.  Subclasses must
   * override this method since the implementation in this abstract
   * class throws an <code>UnsupportedOperationException</code>.
   *
   * @param pixel the specified pixel
   * @param components the array to receive the color and alpha components of the specified pixel
   * @param offset the offset into the <code>components</code> array at which to start storing the
   * color and alpha components
   * @return an array containing the color and alpha components of the specified pixel starting at
   * the specified offset.
   * @throws UnsupportedOperationException if this method is not supported by this
   * <code>ColorModel</code>
   */
  public int[] getComponents(int pixel, int[] components, int offset) {
    throw new UnsupportedOperationException
        ("This method is not supported by this color model.");
  }

  /**
   * Returns an array of unnormalized color/alpha components given a pixel
   * in this <code>ColorModel</code>.  The pixel value is specified by
   * an array of data elements of type transferType passed in as an
   * object reference.  If <code>pixel</code> is not a primitive array
   * of type transferType, a <code>ClassCastException</code> is thrown.
   * An <code>IllegalArgumentException</code> will be thrown if color
   * component values for this <code>ColorModel</code> are not
   * conveniently representable in the unnormalized form.
   * An <code>ArrayIndexOutOfBoundsException</code> is
   * thrown if <code>pixel</code> is not large enough to hold a pixel
   * value for this <code>ColorModel</code>.
   * This method can be used to retrieve the components for a specific
   * pixel value in any <code>ColorModel</code>.  If the components
   * array is <code>null</code>, a new array will be allocated.  The
   * components array will be returned.  Color/alpha components are
   * stored in the <code>components</code> array starting at
   * <code>offset</code> (even if the array is allocated by this
   * method).  An <code>ArrayIndexOutOfBoundsException</code>
   * is thrown if  the components array is not <code>null</code> and is
   * not large enough to hold all the color and alpha components
   * (starting at <code>offset</code>).
   * Since <code>ColorModel</code> is an abstract class,
   * any instance is an instance of a subclass.  Subclasses must
   * override this method since the implementation in this abstract
   * class throws an <code>UnsupportedOperationException</code>.
   *
   * @param pixel the specified pixel
   * @param components an array that receives the color and alpha components of the specified pixel
   * @param offset the index into the <code>components</code> array at which to begin storing the
   * color and alpha components of the specified pixel
   * @return an array containing the color and alpha components of the specified pixel starting at
   * the specified offset.
   * @throws UnsupportedOperationException if this method is not supported by this
   * <code>ColorModel</code>
   */
  public int[] getComponents(Object pixel, int[] components, int offset) {
    throw new UnsupportedOperationException
        ("This method is not supported by this color model.");
  }

  /**
   * Returns an array of all of the color/alpha components in unnormalized
   * form, given a normalized component array.  Unnormalized components
   * are unsigned integral values between 0 and 2<sup>n</sup> - 1, where
   * n is the number of bits for a particular component.  Normalized
   * components are float values between a per component minimum and
   * maximum specified by the <code>ColorSpace</code> object for this
   * <code>ColorModel</code>.  An <code>IllegalArgumentException</code>
   * will be thrown if color component values for this
   * <code>ColorModel</code> are not conveniently representable in the
   * unnormalized form.  If the
   * <code>components</code> array is <code>null</code>, a new array
   * will be allocated.  The <code>components</code> array will
   * be returned.  Color/alpha components are stored in the
   * <code>components</code> array starting at <code>offset</code> (even
   * if the array is allocated by this method). An
   * <code>ArrayIndexOutOfBoundsException</code> is thrown if the
   * <code>components</code> array is not <code>null</code> and is not
   * large enough to hold all the color and alpha
   * components (starting at <code>offset</code>).  An
   * <code>IllegalArgumentException</code> is thrown if the
   * <code>normComponents</code> array is not large enough to hold
   * all the color and alpha components starting at
   * <code>normOffset</code>.
   *
   * @param normComponents an array containing normalized components
   * @param normOffset the offset into the <code>normComponents</code> array at which to start
   * retrieving normalized components
   * @param components an array that receives the components from <code>normComponents</code>
   * @param offset the index into <code>components</code> at which to begin storing normalized
   * components from <code>normComponents</code>
   * @return an array containing unnormalized color and alpha components.
   * @throws IllegalArgumentException If the component values for this <CODE>ColorModel</CODE> are
   * not conveniently representable in the unnormalized form.
   * @throws IllegalArgumentException if the length of <code>normComponents</code> minus
   * <code>normOffset</code> is less than <code>numComponents</code>
   * @throws UnsupportedOperationException if the constructor of this <code>ColorModel</code> called
   * the <code>super(bits)</code> constructor, but did not override this method.  See the
   * constructor, {@link #ColorModel(int)}.
   */
  public int[] getUnnormalizedComponents(float[] normComponents,
      int normOffset,
      int[] components, int offset) {
    // Make sure that someone isn't using a custom color model
    // that called the super(bits) constructor.
    if (colorSpace == null) {
      throw new UnsupportedOperationException("This method is not supported " +
          "by this color model.");
    }

    if (nBits == null) {
      throw new UnsupportedOperationException("This method is not supported.  " +
          "Unable to determine #bits per " +
          "component.");
    }
    if ((normComponents.length - normOffset) < numComponents) {
      throw new
          IllegalArgumentException(
          "Incorrect number of components.  Expecting " +
              numComponents);
    }

    if (components == null) {
      components = new int[offset + numComponents];
    }

    if (supportsAlpha && isAlphaPremultiplied) {
      float normAlpha = normComponents[normOffset + numColorComponents];
      for (int i = 0; i < numColorComponents; i++) {
        components[offset + i] = (int) (normComponents[normOffset + i]
            * ((1 << nBits[i]) - 1)
            * normAlpha + 0.5f);
      }
      components[offset + numColorComponents] = (int)
          (normAlpha * ((1 << nBits[numColorComponents]) - 1) + 0.5f);
    } else {
      for (int i = 0; i < numComponents; i++) {
        components[offset + i] = (int) (normComponents[normOffset + i]
            * ((1 << nBits[i]) - 1) + 0.5f);
      }
    }

    return components;
  }

  /**
   * Returns an array of all of the color/alpha components in normalized
   * form, given an unnormalized component array.  Unnormalized components
   * are unsigned integral values between 0 and 2<sup>n</sup> - 1, where
   * n is the number of bits for a particular component.  Normalized
   * components are float values between a per component minimum and
   * maximum specified by the <code>ColorSpace</code> object for this
   * <code>ColorModel</code>.  An <code>IllegalArgumentException</code>
   * will be thrown if color component values for this
   * <code>ColorModel</code> are not conveniently representable in the
   * unnormalized form.  If the
   * <code>normComponents</code> array is <code>null</code>, a new array
   * will be allocated.  The <code>normComponents</code> array
   * will be returned.  Color/alpha components are stored in the
   * <code>normComponents</code> array starting at
   * <code>normOffset</code> (even if the array is allocated by this
   * method).  An <code>ArrayIndexOutOfBoundsException</code> is thrown
   * if the <code>normComponents</code> array is not <code>null</code>
   * and is not large enough to hold all the color and alpha components
   * (starting at <code>normOffset</code>).  An
   * <code>IllegalArgumentException</code> is thrown if the
   * <code>components</code> array is not large enough to hold all the
   * color and alpha components starting at <code>offset</code>.
   * <p>
   * Since <code>ColorModel</code> is an abstract class,
   * any instance is an instance of a subclass.  The default implementation
   * of this method in this abstract class assumes that component values
   * for this class are conveniently representable in the unnormalized
   * form.  Therefore, subclasses which may
   * have instances which do not support the unnormalized form must
   * override this method.
   *
   * @param components an array containing unnormalized components
   * @param offset the offset into the <code>components</code> array at which to start retrieving
   * unnormalized components
   * @param normComponents an array that receives the normalized components
   * @param normOffset the index into <code>normComponents</code> at which to begin storing
   * normalized components
   * @return an array containing normalized color and alpha components.
   * @throws IllegalArgumentException If the component values for this <CODE>ColorModel</CODE> are
   * not conveniently representable in the unnormalized form.
   * @throws UnsupportedOperationException if the constructor of this <code>ColorModel</code> called
   * the <code>super(bits)</code> constructor, but did not override this method.  See the
   * constructor, {@link #ColorModel(int)}.
   * @throws UnsupportedOperationException if this method is unable to determine the number of bits
   * per component
   */
  public float[] getNormalizedComponents(int[] components, int offset,
      float[] normComponents,
      int normOffset) {
    // Make sure that someone isn't using a custom color model
    // that called the super(bits) constructor.
    if (colorSpace == null) {
      throw new UnsupportedOperationException("This method is not supported by " +
          "this color model.");
    }
    if (nBits == null) {
      throw new UnsupportedOperationException("This method is not supported.  " +
          "Unable to determine #bits per " +
          "component.");
    }

    if ((components.length - offset) < numComponents) {
      throw new
          IllegalArgumentException(
          "Incorrect number of components.  Expecting " +
              numComponents);
    }

    if (normComponents == null) {
      normComponents = new float[numComponents + normOffset];
    }

    if (supportsAlpha && isAlphaPremultiplied) {
      // Normalized coordinates are non premultiplied
      float normAlpha = (float) components[offset + numColorComponents];
      normAlpha /= (float) ((1 << nBits[numColorComponents]) - 1);
      if (normAlpha != 0.0f) {
        for (int i = 0; i < numColorComponents; i++) {
          normComponents[normOffset + i] =
              ((float) components[offset + i]) /
                  (normAlpha * ((float) ((1 << nBits[i]) - 1)));
        }
      } else {
        for (int i = 0; i < numColorComponents; i++) {
          normComponents[normOffset + i] = 0.0f;
        }
      }
      normComponents[normOffset + numColorComponents] = normAlpha;
    } else {
      for (int i = 0; i < numComponents; i++) {
        normComponents[normOffset + i] = ((float) components[offset + i]) /
            ((float) ((1 << nBits[i]) - 1));
      }
    }

    return normComponents;
  }

  /**
   * Returns a pixel value represented as an <code>int</code> in this
   * <code>ColorModel</code>, given an array of unnormalized color/alpha
   * components.  This method will throw an
   * <code>IllegalArgumentException</code> if component values for this
   * <code>ColorModel</code> are not conveniently representable as a
   * single <code>int</code> or if color component values for this
   * <code>ColorModel</code> are not conveniently representable in the
   * unnormalized form.  An
   * <code>ArrayIndexOutOfBoundsException</code> is thrown if  the
   * <code>components</code> array is not large enough to hold all the
   * color and alpha components (starting at <code>offset</code>).
   * Since <code>ColorModel</code> is an abstract class,
   * any instance is an instance of a subclass.  Subclasses must
   * override this method since the implementation in this abstract
   * class throws an <code>UnsupportedOperationException</code>.
   *
   * @param components an array of unnormalized color and alpha components
   * @param offset the index into <code>components</code> at which to begin retrieving the color and
   * alpha components
   * @return an <code>int</code> pixel value in this <code>ColorModel</code> corresponding to the
   * specified components.
   * @throws IllegalArgumentException if pixel values for this <code>ColorModel</code> are not
   * conveniently representable as a single <code>int</code>
   * @throws IllegalArgumentException if component values for this <code>ColorModel</code> are not
   * conveniently representable in the unnormalized form
   * @throws ArrayIndexOutOfBoundsException if the <code>components</code> array is not large enough
   * to hold all of the color and alpha components starting at <code>offset</code>
   * @throws UnsupportedOperationException if this method is not supported by this
   * <code>ColorModel</code>
   */
  public int getDataElement(int[] components, int offset) {
    throw new UnsupportedOperationException("This method is not supported " +
        "by this color model.");
  }

  /**
   * Returns a data element array representation of a pixel in this
   * <code>ColorModel</code>, given an array of unnormalized color/alpha
   * components.  This array can then be passed to the
   * <code>setDataElements</code> method of a <code>WritableRaster</code>
   * object.  This method will throw an <code>IllegalArgumentException</code>
   * if color component values for this <code>ColorModel</code> are not
   * conveniently representable in the unnormalized form.
   * An <code>ArrayIndexOutOfBoundsException</code> is thrown
   * if the <code>components</code> array is not large enough to hold
   * all the color and alpha components (starting at
   * <code>offset</code>).  If the <code>obj</code> variable is
   * <code>null</code>, a new array will be allocated.  If
   * <code>obj</code> is not <code>null</code>, it must be a primitive
   * array of type transferType; otherwise, a
   * <code>ClassCastException</code> is thrown.  An
   * <code>ArrayIndexOutOfBoundsException</code> is thrown if
   * <code>obj</code> is not large enough to hold a pixel value for this
   * <code>ColorModel</code>.
   * Since <code>ColorModel</code> is an abstract class,
   * any instance is an instance of a subclass.  Subclasses must
   * override this method since the implementation in this abstract
   * class throws an <code>UnsupportedOperationException</code>.
   *
   * @param components an array of unnormalized color and alpha components
   * @param offset the index into <code>components</code> at which to begin retrieving color and
   * alpha components
   * @param obj the <code>Object</code> representing an array of color and alpha components
   * @return an <code>Object</code> representing an array of color and alpha components.
   * @throws ClassCastException if <code>obj</code> is not a primitive array of type
   * <code>transferType</code>
   * @throws ArrayIndexOutOfBoundsException if <code>obj</code> is not large enough to hold a pixel
   * value for this <code>ColorModel</code> or the <code>components</code> array is not large enough
   * to hold all of the color and alpha components starting at <code>offset</code>
   * @throws IllegalArgumentException if component values for this <code>ColorModel</code> are not
   * conveniently representable in the unnormalized form
   * @throws UnsupportedOperationException if this method is not supported by this
   * <code>ColorModel</code>
   * @see WritableRaster#setDataElements
   * @see SampleModel#setDataElements
   */
  public Object getDataElements(int[] components, int offset, Object obj) {
    throw new UnsupportedOperationException("This method has not been implemented " +
        "for this color model.");
  }

  /**
   * Returns a pixel value represented as an <code>int</code> in this
   * <code>ColorModel</code>, given an array of normalized color/alpha
   * components.  This method will throw an
   * <code>IllegalArgumentException</code> if pixel values for this
   * <code>ColorModel</code> are not conveniently representable as a
   * single <code>int</code>.  An
   * <code>ArrayIndexOutOfBoundsException</code> is thrown if  the
   * <code>normComponents</code> array is not large enough to hold all the
   * color and alpha components (starting at <code>normOffset</code>).
   * Since <code>ColorModel</code> is an abstract class,
   * any instance is an instance of a subclass.  The default implementation
   * of this method in this abstract class first converts from the
   * normalized form to the unnormalized form and then calls
   * <code>getDataElement(int[], int)</code>.  Subclasses which may
   * have instances which do not support the unnormalized form must
   * override this method.
   *
   * @param normComponents an array of normalized color and alpha components
   * @param normOffset the index into <code>normComponents</code> at which to begin retrieving the
   * color and alpha components
   * @return an <code>int</code> pixel value in this <code>ColorModel</code> corresponding to the
   * specified components.
   * @throws IllegalArgumentException if pixel values for this <code>ColorModel</code> are not
   * conveniently representable as a single <code>int</code>
   * @throws ArrayIndexOutOfBoundsException if the <code>normComponents</code> array is not large
   * enough to hold all of the color and alpha components starting at <code>normOffset</code>
   * @since 1.4
   */
  public int getDataElement(float[] normComponents, int normOffset) {
    int components[] = getUnnormalizedComponents(normComponents,
        normOffset, null, 0);
    return getDataElement(components, 0);
  }

  /**
   * Returns a data element array representation of a pixel in this
   * <code>ColorModel</code>, given an array of normalized color/alpha
   * components.  This array can then be passed to the
   * <code>setDataElements</code> method of a <code>WritableRaster</code>
   * object.  An <code>ArrayIndexOutOfBoundsException</code> is thrown
   * if the <code>normComponents</code> array is not large enough to hold
   * all the color and alpha components (starting at
   * <code>normOffset</code>).  If the <code>obj</code> variable is
   * <code>null</code>, a new array will be allocated.  If
   * <code>obj</code> is not <code>null</code>, it must be a primitive
   * array of type transferType; otherwise, a
   * <code>ClassCastException</code> is thrown.  An
   * <code>ArrayIndexOutOfBoundsException</code> is thrown if
   * <code>obj</code> is not large enough to hold a pixel value for this
   * <code>ColorModel</code>.
   * Since <code>ColorModel</code> is an abstract class,
   * any instance is an instance of a subclass.  The default implementation
   * of this method in this abstract class first converts from the
   * normalized form to the unnormalized form and then calls
   * <code>getDataElement(int[], int, Object)</code>.  Subclasses which may
   * have instances which do not support the unnormalized form must
   * override this method.
   *
   * @param normComponents an array of normalized color and alpha components
   * @param normOffset the index into <code>normComponents</code> at which to begin retrieving color
   * and alpha components
   * @param obj a primitive data array to hold the returned pixel
   * @return an <code>Object</code> which is a primitive data array representation of a pixel
   * @throws ClassCastException if <code>obj</code> is not a primitive array of type
   * <code>transferType</code>
   * @throws ArrayIndexOutOfBoundsException if <code>obj</code> is not large enough to hold a pixel
   * value for this <code>ColorModel</code> or the <code>normComponents</code> array is not large
   * enough to hold all of the color and alpha components starting at <code>normOffset</code>
   * @see WritableRaster#setDataElements
   * @see SampleModel#setDataElements
   * @since 1.4
   */
  public Object getDataElements(float[] normComponents, int normOffset,
      Object obj) {
    int components[] = getUnnormalizedComponents(normComponents,
        normOffset, null, 0);
    return getDataElements(components, 0, obj);
  }

  /**
   * Returns an array of all of the color/alpha components in normalized
   * form, given a pixel in this <code>ColorModel</code>.  The pixel
   * value is specified by an array of data elements of type transferType
   * passed in as an object reference.  If pixel is not a primitive array
   * of type transferType, a <code>ClassCastException</code> is thrown.
   * An <code>ArrayIndexOutOfBoundsException</code> is thrown if
   * <code>pixel</code> is not large enough to hold a pixel value for this
   * <code>ColorModel</code>.
   * Normalized components are float values between a per component minimum
   * and maximum specified by the <code>ColorSpace</code> object for this
   * <code>ColorModel</code>.  If the
   * <code>normComponents</code> array is <code>null</code>, a new array
   * will be allocated.  The <code>normComponents</code> array
   * will be returned.  Color/alpha components are stored in the
   * <code>normComponents</code> array starting at
   * <code>normOffset</code> (even if the array is allocated by this
   * method).  An <code>ArrayIndexOutOfBoundsException</code> is thrown
   * if the <code>normComponents</code> array is not <code>null</code>
   * and is not large enough to hold all the color and alpha components
   * (starting at <code>normOffset</code>).
   * Since <code>ColorModel</code> is an abstract class,
   * any instance is an instance of a subclass.  The default implementation
   * of this method in this abstract class first retrieves color and alpha
   * components in the unnormalized form using
   * <code>getComponents(Object, int[], int)</code> and then calls
   * <code>getNormalizedComponents(int[], int, float[], int)</code>.
   * Subclasses which may
   * have instances which do not support the unnormalized form must
   * override this method.
   *
   * @param pixel the specified pixel
   * @param normComponents an array to receive the normalized components
   * @param normOffset the offset into the <code>normComponents</code> array at which to start
   * storing normalized components
   * @return an array containing normalized color and alpha components.
   * @throws ClassCastException if <code>pixel</code> is not a primitive array of type transferType
   * @throws ArrayIndexOutOfBoundsException if <code>normComponents</code> is not large enough to
   * hold all color and alpha components starting at <code>normOffset</code>
   * @throws ArrayIndexOutOfBoundsException if <code>pixel</code> is not large enough to hold a
   * pixel value for this <code>ColorModel</code>.
   * @throws UnsupportedOperationException if the constructor of this <code>ColorModel</code> called
   * the <code>super(bits)</code> constructor, but did not override this method.  See the
   * constructor, {@link #ColorModel(int)}.
   * @throws UnsupportedOperationException if this method is unable to determine the number of bits
   * per component
   * @since 1.4
   */
  public float[] getNormalizedComponents(Object pixel,
      float[] normComponents,
      int normOffset) {
    int components[] = getComponents(pixel, null, 0);
    return getNormalizedComponents(components, 0,
        normComponents, normOffset);
  }

  /**
   * Tests if the specified <code>Object</code> is an instance of
   * <code>ColorModel</code> and if it equals this
   * <code>ColorModel</code>.
   *
   * @param obj the <code>Object</code> to test for equality
   * @return <code>true</code> if the specified <code>Object</code> is an instance of
   * <code>ColorModel</code> and equals this <code>ColorModel</code>; <code>false</code> otherwise.
   */
  public boolean equals(Object obj) {
    if (!(obj instanceof ColorModel)) {
      return false;
    }
    ColorModel cm = (ColorModel) obj;

    if (this == cm) {
      return true;
    }
    if (supportsAlpha != cm.hasAlpha() ||
        isAlphaPremultiplied != cm.isAlphaPremultiplied() ||
        pixel_bits != cm.getPixelSize() ||
        transparency != cm.getTransparency() ||
        numComponents != cm.getNumComponents()) {
      return false;
    }

    int[] nb = cm.getComponentSize();

    if ((nBits != null) && (nb != null)) {
      for (int i = 0; i < numComponents; i++) {
        if (nBits[i] != nb[i]) {
          return false;
        }
      }
    } else {
      return ((nBits == null) && (nb == null));
    }

    return true;
  }

  /**
   * Returns the hash code for this ColorModel.
   *
   * @return a hash code for this ColorModel.
   */
  public int hashCode() {

    int result = 0;

    result = (supportsAlpha ? 2 : 3) +
        (isAlphaPremultiplied ? 4 : 5) +
        pixel_bits * 6 +
        transparency * 7 +
        numComponents * 8;

    if (nBits != null) {
      for (int i = 0; i < numComponents; i++) {
        result = result + nBits[i] * (i + 9);
      }
    }

    return result;
  }

  /**
   * Returns the <code>ColorSpace</code> associated with this
   * <code>ColorModel</code>.
   *
   * @return the <code>ColorSpace</code> of this <code>ColorModel</code>.
   */
  final public ColorSpace getColorSpace() {
    return colorSpace;
  }

  /**
   * Forces the raster data to match the state specified in the
   * <code>isAlphaPremultiplied</code> variable, assuming the data is
   * currently correctly described by this <code>ColorModel</code>.  It
   * may multiply or divide the color raster data by alpha, or do
   * nothing if the data is in the correct state.  If the data needs to
   * be coerced, this method will also return an instance of this
   * <code>ColorModel</code> with the <code>isAlphaPremultiplied</code>
   * flag set appropriately.  This method will throw a
   * <code>UnsupportedOperationException</code> if it is not supported
   * by this <code>ColorModel</code>.
   * Since <code>ColorModel</code> is an abstract class,
   * any instance is an instance of a subclass.  Subclasses must
   * override this method since the implementation in this abstract
   * class throws an <code>UnsupportedOperationException</code>.
   *
   * @param raster the <code>WritableRaster</code> data
   * @param isAlphaPremultiplied <code>true</code> if the alpha is premultiplied; <code>false</code>
   * otherwise
   * @return a <code>ColorModel</code> object that represents the coerced data.
   */
  public ColorModel coerceData(WritableRaster raster,
      boolean isAlphaPremultiplied) {
    throw new UnsupportedOperationException
        ("This method is not supported by this color model");
  }

  /**
   * Returns <code>true</code> if <code>raster</code> is compatible
   * with this <code>ColorModel</code> and <code>false</code> if it is
   * not.
   * Since <code>ColorModel</code> is an abstract class,
   * any instance is an instance of a subclass.  Subclasses must
   * override this method since the implementation in this abstract
   * class throws an <code>UnsupportedOperationException</code>.
   *
   * @param raster the {@link Raster} object to test for compatibility
   * @return <code>true</code> if <code>raster</code> is compatible with this
   * <code>ColorModel</code>.
   * @throws UnsupportedOperationException if this method has not been implemented for this
   * <code>ColorModel</code>
   */
  public boolean isCompatibleRaster(Raster raster) {
    throw new UnsupportedOperationException(
        "This method has not been implemented for this ColorModel.");
  }

  /**
   * Creates a <code>WritableRaster</code> with the specified width and
   * height that has a data layout (<code>SampleModel</code>) compatible
   * with this <code>ColorModel</code>.
   * Since <code>ColorModel</code> is an abstract class,
   * any instance is an instance of a subclass.  Subclasses must
   * override this method since the implementation in this abstract
   * class throws an <code>UnsupportedOperationException</code>.
   *
   * @param w the width to apply to the new <code>WritableRaster</code>
   * @param h the height to apply to the new <code>WritableRaster</code>
   * @return a <code>WritableRaster</code> object with the specified width and height.
   * @throws UnsupportedOperationException if this method is not supported by this
   * <code>ColorModel</code>
   * @see WritableRaster
   * @see SampleModel
   */
  public WritableRaster createCompatibleWritableRaster(int w, int h) {
    throw new UnsupportedOperationException
        ("This method is not supported by this color model");
  }

  /**
   * Creates a <code>SampleModel</code> with the specified width and
   * height that has a data layout compatible with this
   * <code>ColorModel</code>.
   * Since <code>ColorModel</code> is an abstract class,
   * any instance is an instance of a subclass.  Subclasses must
   * override this method since the implementation in this abstract
   * class throws an <code>UnsupportedOperationException</code>.
   *
   * @param w the width to apply to the new <code>SampleModel</code>
   * @param h the height to apply to the new <code>SampleModel</code>
   * @return a <code>SampleModel</code> object with the specified width and height.
   * @throws UnsupportedOperationException if this method is not supported by this
   * <code>ColorModel</code>
   * @see SampleModel
   */
  public SampleModel createCompatibleSampleModel(int w, int h) {
    throw new UnsupportedOperationException
        ("This method is not supported by this color model");
  }

  /**
   * Checks if the <code>SampleModel</code> is compatible with this
   * <code>ColorModel</code>.
   * Since <code>ColorModel</code> is an abstract class,
   * any instance is an instance of a subclass.  Subclasses must
   * override this method since the implementation in this abstract
   * class throws an <code>UnsupportedOperationException</code>.
   *
   * @param sm the specified <code>SampleModel</code>
   * @return <code>true</code> if the specified <code>SampleModel</code> is compatible with this
   * <code>ColorModel</code>; <code>false</code> otherwise.
   * @throws UnsupportedOperationException if this method is not supported by this
   * <code>ColorModel</code>
   * @see SampleModel
   */
  public boolean isCompatibleSampleModel(SampleModel sm) {
    throw new UnsupportedOperationException
        ("This method is not supported by this color model");
  }

  /**
   * Disposes of system resources associated with this
   * <code>ColorModel</code> once this <code>ColorModel</code> is no
   * longer referenced.
   */
  public void finalize() {
  }


  /**
   * Returns a <code>Raster</code> representing the alpha channel of an
   * image, extracted from the input <code>Raster</code>, provided that
   * pixel values of this <code>ColorModel</code> represent color and
   * alpha information as separate spatial bands (e.g.
   * {@link ComponentColorModel} and <code>DirectColorModel</code>).
   * This method assumes that <code>Raster</code> objects associated
   * with such a <code>ColorModel</code> store the alpha band, if
   * present, as the last band of image data.  Returns <code>null</code>
   * if there is no separate spatial alpha channel associated with this
   * <code>ColorModel</code>.  If this is an
   * <code>IndexColorModel</code> which has alpha in the lookup table,
   * this method will return <code>null</code> since
   * there is no spatially discrete alpha channel.
   * This method will create a new <code>Raster</code> (but will share
   * the data array).
   * Since <code>ColorModel</code> is an abstract class, any instance
   * is an instance of a subclass.  Subclasses must override this
   * method to get any behavior other than returning <code>null</code>
   * because the implementation in this abstract class returns
   * <code>null</code>.
   *
   * @param raster the specified <code>Raster</code>
   * @return a <code>Raster</code> representing the alpha channel of an image, obtained from the
   * specified <code>Raster</code>.
   */
  public WritableRaster getAlphaRaster(WritableRaster raster) {
    return null;
  }

  /**
   * Returns the <code>String</code> representation of the contents of
   * this <code>ColorModel</code>object.
   *
   * @return a <code>String</code> representing the contents of this <code>ColorModel</code> object.
   */
  public String toString() {
    return new String("ColorModel: #pixelBits = " + pixel_bits
        + " numComponents = " + numComponents
        + " color space = " + colorSpace
        + " transparency = " + transparency
        + " has alpha = " + supportsAlpha
        + " isAlphaPre = " + isAlphaPremultiplied
    );
  }

  static int getDefaultTransferType(int pixel_bits) {
    if (pixel_bits <= 8) {
      return DataBuffer.TYPE_BYTE;
    } else if (pixel_bits <= 16) {
      return DataBuffer.TYPE_USHORT;
    } else if (pixel_bits <= 32) {
      return DataBuffer.TYPE_INT;
    } else {
      return DataBuffer.TYPE_UNDEFINED;
    }
  }

  static byte[] l8Tos8 = null;   // 8-bit linear to 8-bit non-linear sRGB LUT
  static byte[] s8Tol8 = null;   // 8-bit non-linear sRGB to 8-bit linear LUT
  static byte[] l16Tos8 = null;  // 16-bit linear to 8-bit non-linear sRGB LUT
  static short[] s8Tol16 = null; // 8-bit non-linear sRGB to 16-bit linear LUT

  // Maps to hold LUTs for grayscale conversions
  static Map<ICC_ColorSpace, byte[]> g8Tos8Map = null;     // 8-bit gray values to 8-bit sRGB values
  static Map<ICC_ColorSpace, byte[]> lg16Toog8Map = null;  // 16-bit linear to 8-bit "other" gray
  static Map<ICC_ColorSpace, byte[]> g16Tos8Map = null;    // 16-bit gray values to 8-bit sRGB values
  static Map<ICC_ColorSpace, short[]> lg16Toog16Map = null; // 16-bit linear to 16-bit "other" gray

  static boolean isLinearRGBspace(ColorSpace cs) {
    // Note: CMM.LINEAR_RGBspace will be null if the linear
    // RGB space has not been created yet.
    return (cs == CMSManager.LINEAR_RGBspace);
  }

  static boolean isLinearGRAYspace(ColorSpace cs) {
    // Note: CMM.GRAYspace will be null if the linear
    // gray space has not been created yet.
    return (cs == CMSManager.GRAYspace);
  }

  static byte[] getLinearRGB8TosRGB8LUT() {
    if (l8Tos8 == null) {
      l8Tos8 = new byte[256];
      float input, output;
      // algorithm for linear RGB to nonlinear sRGB conversion
      // is from the IEC 61966-2-1 International Standard,
      // Colour Management - Default RGB colour space - sRGB,
      // First Edition, 1999-10,
      // avaiable for order at http://www.iec.ch
      for (int i = 0; i <= 255; i++) {
        input = ((float) i) / 255.0f;
        if (input <= 0.0031308f) {
          output = input * 12.92f;
        } else {
          output = 1.055f * ((float) Math.pow(input, (1.0 / 2.4)))
              - 0.055f;
        }
        l8Tos8[i] = (byte) Math.round(output * 255.0f);
      }
    }
    return l8Tos8;
  }

  static byte[] getsRGB8ToLinearRGB8LUT() {
    if (s8Tol8 == null) {
      s8Tol8 = new byte[256];
      float input, output;
      // algorithm from IEC 61966-2-1 International Standard
      for (int i = 0; i <= 255; i++) {
        input = ((float) i) / 255.0f;
        if (input <= 0.04045f) {
          output = input / 12.92f;
        } else {
          output = (float) Math.pow((input + 0.055f) / 1.055f, 2.4);
        }
        s8Tol8[i] = (byte) Math.round(output * 255.0f);
      }
    }
    return s8Tol8;
  }

  static byte[] getLinearRGB16TosRGB8LUT() {
    if (l16Tos8 == null) {
      l16Tos8 = new byte[65536];
      float input, output;
      // algorithm from IEC 61966-2-1 International Standard
      for (int i = 0; i <= 65535; i++) {
        input = ((float) i) / 65535.0f;
        if (input <= 0.0031308f) {
          output = input * 12.92f;
        } else {
          output = 1.055f * ((float) Math.pow(input, (1.0 / 2.4)))
              - 0.055f;
        }
        l16Tos8[i] = (byte) Math.round(output * 255.0f);
      }
    }
    return l16Tos8;
  }

  static short[] getsRGB8ToLinearRGB16LUT() {
    if (s8Tol16 == null) {
      s8Tol16 = new short[256];
      float input, output;
      // algorithm from IEC 61966-2-1 International Standard
      for (int i = 0; i <= 255; i++) {
        input = ((float) i) / 255.0f;
        if (input <= 0.04045f) {
          output = input / 12.92f;
        } else {
          output = (float) Math.pow((input + 0.055f) / 1.055f, 2.4);
        }
        s8Tol16[i] = (short) Math.round(output * 65535.0f);
      }
    }
    return s8Tol16;
  }

  /*
   * Return a byte LUT that converts 8-bit gray values in the grayCS
   * ColorSpace to the appropriate 8-bit sRGB value.  I.e., if lut
   * is the byte array returned by this method and sval = lut[gval],
   * then the sRGB triple (sval,sval,sval) is the best match to gval.
   * Cache references to any computed LUT in a Map.
   */
  static byte[] getGray8TosRGB8LUT(ICC_ColorSpace grayCS) {
    if (isLinearGRAYspace(grayCS)) {
      return getLinearRGB8TosRGB8LUT();
    }
    if (g8Tos8Map != null) {
      byte[] g8Tos8LUT = g8Tos8Map.get(grayCS);
      if (g8Tos8LUT != null) {
        return g8Tos8LUT;
      }
    }
    byte[] g8Tos8LUT = new byte[256];
    for (int i = 0; i <= 255; i++) {
      g8Tos8LUT[i] = (byte) i;
    }
    ColorTransform[] transformList = new ColorTransform[2];
    PCMM mdl = CMSManager.getModule();
    ICC_ColorSpace srgbCS =
        (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_sRGB);
    transformList[0] = mdl.createTransform(
        grayCS.getProfile(), ColorTransform.Any, ColorTransform.In);
    transformList[1] = mdl.createTransform(
        srgbCS.getProfile(), ColorTransform.Any, ColorTransform.Out);
    ColorTransform t = mdl.createTransform(transformList);
    byte[] tmp = t.colorConvert(g8Tos8LUT, null);
    for (int i = 0, j = 2; i <= 255; i++, j += 3) {
      // All three components of tmp should be equal, since
      // the input color space to colorConvert is a gray scale
      // space.  However, there are slight anomalies in the results.
      // Copy tmp starting at index 2, since colorConvert seems
      // to be slightly more accurate for the third component!
      g8Tos8LUT[i] = tmp[j];
    }
    if (g8Tos8Map == null) {
      g8Tos8Map = Collections.synchronizedMap(new WeakHashMap<ICC_ColorSpace, byte[]>(2));
    }
    g8Tos8Map.put(grayCS, g8Tos8LUT);
    return g8Tos8LUT;
  }

  /*
   * Return a byte LUT that converts 16-bit gray values in the CS_GRAY
   * linear gray ColorSpace to the appropriate 8-bit value in the
   * grayCS ColorSpace.  Cache references to any computed LUT in a Map.
   */
  static byte[] getLinearGray16ToOtherGray8LUT(ICC_ColorSpace grayCS) {
    if (lg16Toog8Map != null) {
      byte[] lg16Toog8LUT = lg16Toog8Map.get(grayCS);
      if (lg16Toog8LUT != null) {
        return lg16Toog8LUT;
      }
    }
    short[] tmp = new short[65536];
    for (int i = 0; i <= 65535; i++) {
      tmp[i] = (short) i;
    }
    ColorTransform[] transformList = new ColorTransform[2];
    PCMM mdl = CMSManager.getModule();
    ICC_ColorSpace lgCS =
        (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_GRAY);
    transformList[0] = mdl.createTransform(
        lgCS.getProfile(), ColorTransform.Any, ColorTransform.In);
    transformList[1] = mdl.createTransform(
        grayCS.getProfile(), ColorTransform.Any, ColorTransform.Out);
    ColorTransform t = mdl.createTransform(transformList);
    tmp = t.colorConvert(tmp, null);
    byte[] lg16Toog8LUT = new byte[65536];
    for (int i = 0; i <= 65535; i++) {
      // scale unsigned short (0 - 65535) to unsigned byte (0 - 255)
      lg16Toog8LUT[i] =
          (byte) (((float) (tmp[i] & 0xffff)) * (1.0f / 257.0f) + 0.5f);
    }
    if (lg16Toog8Map == null) {
      lg16Toog8Map = Collections.synchronizedMap(new WeakHashMap<ICC_ColorSpace, byte[]>(2));
    }
    lg16Toog8Map.put(grayCS, lg16Toog8LUT);
    return lg16Toog8LUT;
  }

  /*
   * Return a byte LUT that converts 16-bit gray values in the grayCS
   * ColorSpace to the appropriate 8-bit sRGB value.  I.e., if lut
   * is the byte array returned by this method and sval = lut[gval],
   * then the sRGB triple (sval,sval,sval) is the best match to gval.
   * Cache references to any computed LUT in a Map.
   */
  static byte[] getGray16TosRGB8LUT(ICC_ColorSpace grayCS) {
    if (isLinearGRAYspace(grayCS)) {
      return getLinearRGB16TosRGB8LUT();
    }
    if (g16Tos8Map != null) {
      byte[] g16Tos8LUT = g16Tos8Map.get(grayCS);
      if (g16Tos8LUT != null) {
        return g16Tos8LUT;
      }
    }
    short[] tmp = new short[65536];
    for (int i = 0; i <= 65535; i++) {
      tmp[i] = (short) i;
    }
    ColorTransform[] transformList = new ColorTransform[2];
    PCMM mdl = CMSManager.getModule();
    ICC_ColorSpace srgbCS =
        (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_sRGB);
    transformList[0] = mdl.createTransform(
        grayCS.getProfile(), ColorTransform.Any, ColorTransform.In);
    transformList[1] = mdl.createTransform(
        srgbCS.getProfile(), ColorTransform.Any, ColorTransform.Out);
    ColorTransform t = mdl.createTransform(transformList);
    tmp = t.colorConvert(tmp, null);
    byte[] g16Tos8LUT = new byte[65536];
    for (int i = 0, j = 2; i <= 65535; i++, j += 3) {
      // All three components of tmp should be equal, since
      // the input color space to colorConvert is a gray scale
      // space.  However, there are slight anomalies in the results.
      // Copy tmp starting at index 2, since colorConvert seems
      // to be slightly more accurate for the third component!

      // scale unsigned short (0 - 65535) to unsigned byte (0 - 255)
      g16Tos8LUT[i] =
          (byte) (((float) (tmp[j] & 0xffff)) * (1.0f / 257.0f) + 0.5f);
    }
    if (g16Tos8Map == null) {
      g16Tos8Map = Collections.synchronizedMap(new WeakHashMap<ICC_ColorSpace, byte[]>(2));
    }
    g16Tos8Map.put(grayCS, g16Tos8LUT);
    return g16Tos8LUT;
  }

  /*
   * Return a short LUT that converts 16-bit gray values in the CS_GRAY
   * linear gray ColorSpace to the appropriate 16-bit value in the
   * grayCS ColorSpace.  Cache references to any computed LUT in a Map.
   */
  static short[] getLinearGray16ToOtherGray16LUT(ICC_ColorSpace grayCS) {
    if (lg16Toog16Map != null) {
      short[] lg16Toog16LUT = lg16Toog16Map.get(grayCS);
      if (lg16Toog16LUT != null) {
        return lg16Toog16LUT;
      }
    }
    short[] tmp = new short[65536];
    for (int i = 0; i <= 65535; i++) {
      tmp[i] = (short) i;
    }
    ColorTransform[] transformList = new ColorTransform[2];
    PCMM mdl = CMSManager.getModule();
    ICC_ColorSpace lgCS =
        (ICC_ColorSpace) ColorSpace.getInstance(ColorSpace.CS_GRAY);
    transformList[0] = mdl.createTransform(
        lgCS.getProfile(), ColorTransform.Any, ColorTransform.In);
    transformList[1] = mdl.createTransform(
        grayCS.getProfile(), ColorTransform.Any, ColorTransform.Out);
    ColorTransform t = mdl.createTransform(
        transformList);
    short[] lg16Toog16LUT = t.colorConvert(tmp, null);
    if (lg16Toog16Map == null) {
      lg16Toog16Map = Collections.synchronizedMap(new WeakHashMap<ICC_ColorSpace, short[]>(2));
    }
    lg16Toog16Map.put(grayCS, lg16Toog16LUT);
    return lg16Toog16LUT;
  }

}
